(ns intro.visualization
(:require [scicloj.kind-clerk.api :as kind-clerk]
[tablecloth.api :as tc]
[aerial.hanami.common :as hc]
[aerial.hanami.templates :as ht]
[scicloj.noj.v1.vis.hanami.templates :as vht]
[scicloj.noj.v1.vis :as vis]
[scicloj.noj.v1.stats :as stats]
[tech.v3.datatype :as dtype]
[tech.v3.datatype.functional :as fun]
[scicloj.kindly.v3.api :as kindly]
[scicloj.kindly.v3.kind :as kind]
[hiccup.core :as hiccup]
[clojure2d.color :as color]))
…
Raw html
(-> "<p>Hello, <i>Noj</i>.</p>"
vis/raw-html)
…
(-> [:svg {:height 210
:width 500}
[:line {:x1 0
:y1 0
:x2 200
:y2 200
:style "stroke:rgb(255,0,0);stroke-width:2"}]]
hiccup/html
vis/raw-html)
…
Visualizing datases with Hanami
Noj offers a few convenience functions to make Hanami plotting work smoothly with Tablecloth and Kindly.
(def mtcars
(-> "data/mtcars.csv"
(tc/dataset {:key-fn keyword})))
…
(def iris
(-> "data/iris.csv"
(tc/dataset {:key-fn keyword})))
…
(def random-walk
(let [n 20]
(-> {:x (range n)
:y (->> (repeatedly n #(- (rand) 0.5))
(reductions +))}
tc/dataset)))
…
A simple plot
We can plot a Tablecloth datasete using a Hanami template:
(-> random-walk
(vis/hanami-plot ht/point-chart
{:MSIZE 200}))
…
Let us look inside the resulting vega-lite space. We can see the dataset is included as CSV:
(-> random-walk
(vis/hanami-plot ht/point-chart
{:MSIZE 200})
kind/pprint){:encoding
{:y {:field "y", :type "quantitative"},
:x {:field "x", :type "quantitative"}},
:mark {:type "circle", :size 200, :tooltip true},
:width 400,
:background "floralwhite",
:height 300,
:data
{:values
"x,y\n0,-0.39000702940466614\n1,-0.28078830153194767\n2,-0.18583467903466833\n3,-0.2577314731282059\n4,-0.05568965021095229\n5,0.4063180065168299\n6,0.8730106031755561\n7,0.8287196401375647\n8,0.5538425075901422\n9,0.3321782149703274\n10,0.7225877270515254\n11,1.1963059146411112\n12,1.6199951810274895\n13,1.2749814756515754\n14,1.5920234461797944\n15,1.4704584756897248\n16,1.890674581112711\n17,2.0918163699328622\n18,1.6509294208303236\n19,1.7701223947136426\n",
:format {:type "csv"}}}Additional Hanami templates
The scicloj.noj.v1.vis.hanami.templates namespace add Hanami templates to Hanami’s own collection.
(-> mtcars
(vis/hanami-plot vht/boxplot-chart
{:X :gear
:XTYPE :nominal
:Y :mpg}))
…
Layers
(-> random-walk
(vis/hanami-layers
{:TITLE "points and a line"}
[(vis/hanami-plot nil
ht/point-chart
{:MSIZE 400})
(vis/hanami-plot nil
ht/line-chart
{:MSIZE 4
:MCOLOR "brown"})]))
…
Concatenation
(-> random-walk
(vis/hanami-vconcat
{}
[(vis/hanami-plot nil
ht/point-chart
{:MSIZE 400
:HEIGHT 100
:WIDTH 100})
(vis/hanami-plot nil
ht/line-chart
{:MSIZE 4
:MCOLOR "brown"
:HEIGHT 100
:WIDTH 100})]))
…
(-> random-walk
(vis/hanami-hconcat
{}
[(vis/hanami-plot nil
ht/point-chart
{:MSIZE 400
:HEIGHT 100
:WIDTH 100})
(vis/hanami-plot nil
ht/line-chart
{:MSIZE 4
:MCOLOR "brown"
:HEIGHT 100
:WIDTH 100})]))
…
Linear regression
(-> mtcars
(stats/add-predictions :mpg [:wt]
{:model-type :smile.regression/ordinary-least-square})
(vis/hanami-layers {}
[(vis/hanami-plot nil
ht/point-chart
{:X :wt
:Y :mpg
:MSIZE 200
:HEIGHT 200
:WIDTH 200})
(vis/hanami-plot nil
ht/line-chart
{:X :wt
:Y :mpg-prediction
:MSIZE 5
:MCOLOR "purple"
:YTITLE :mpg})]))
…
Histogram
(-> iris
(vis/hanami-histogram :sepal-width
{:nbins 10}))
…
Combining a few things together
The following is inspired by the example at Plotnine’s main page. Note how we add regression lines here.
(let [pallete (->> :accent
color/palette
(mapv color/format-hex))]
(-> mtcars
(tc/group-by :gear {:result-type :as-map})
(->> (sort-by key)
(map-indexed
(fn [i [group-name ds]]
(-> ds
(stats/add-predictions :mpg [:wt]
{:model-type :smile.regression/ordinary-least-square})
(vis/hanami-layers {:TITLE (str "grear=" group-name)}
[(vis/hanami-plot nil
ht/point-chart
{:X :wt
:Y :mpg
:MSIZE 200
:MCOLOR (pallete i)
:HEIGHT 200
:WIDTH 200})
(vis/hanami-plot nil
ht/line-chart
{:X :wt
:Y :mpg-prediction
:MSIZE 5
:MCOLOR (pallete i)
:YTITLE :mpg})]
))))
(vis/hanami-hconcat nil {}))))
…
(let [pallete (->> :accent
color/palette
(mapv color/format-hex))]
(-> iris
(tc/group-by :species {:result-type :as-map})
(->> (sort-by key)
(map-indexed
(fn [i [group-name ds]]
(-> ds
(vis/hanami-histogram :sepal-width
{:nbins 10}))))
(vis/hanami-vconcat nil {}))))
…
:bye:bye